
#include<time.h>
#include<stdlib.h>
#include"raytracer.h"
#include"ray.h"
#include"algfloat.h"

bool RayTracer::Collide(Ray * ray)
{
  bool shadow = ray->shadow_mode;
  Object** objs = obj_array.data;
  int n = obj_array.nData;
  if(shadow){
    for(int i=0;i<n;i++){
      if(ray->shadow_obj!=i && objs[i]->Hit(ray)){
        //printf("shadow obj = %d\n", ray->shadow_obj);
        //printf("shadow ");
        return true;
      }
    }
  }
  else{
    bool ret = false;
    for(int i=0;i<n;i++){
      if(objs[i]->Hit(ray)){
        ret=true;
        ray->shadow_obj=i;
      }
    }
    return ret;
  }
  return false;
}

void RayTracer::LightShade (Ray * ray)
{
  Light ** lights = light_array.data;
  int n = light_array.nData;
  ray->shadow_mode = true;
  //printf("LightShade n=%d\n",n);
  float tcolor[3]={0.0,0.0,0.0};
  float * obj_color = ray->obj_color;
  float percent = 1-ray->obj_reflec;
  float lqt = 1.0/n;
  for(int i=0;i<n;i++){
    lights[i]->SetupRay(ray);
    float dot = AFDot(ray->GetD(SHADOW_RAY),ray->normal);
    //if(dot>0)
      //printf("dot>0 collide %s ", );
    if(dot>0 && !Collide(ray)){
      //printf("Chegou dot %f\n",dot);
      float * lt = ray->GetD(SHADOW_RAY);
      float * v = ray->GetD(VIEW_RAY);
      float half[4] = {lt[0]-v[0],lt[1]-v[1],lt[2]-v[2],1.0};
      //float half[4] = {lt[0]*2,lt[1]*2,lt[2]*2,1.0};
      AFUnit(half);
      //printf("spec_dot %f spec %f\n",spec_dot, spec);
      tcolor[0] += obj_color[0]*dot*lqt*percent;
      tcolor[1] += obj_color[1]*dot*lqt*percent;
      tcolor[2] += obj_color[2]*dot*lqt*percent;
      if(!ray->obj_nospec){
        AFUnit(ray->normal);
        float spec_dot = AFDot(half,ray->normal);
        float spec = pow(spec_dot>0?spec_dot:0.0, ray->obj_shine);
        //printf("spec_dot %f spec %f shine %f ? %f res %f\n", spec_dot, spec,ray->obj_shine, spec_dot>0?spec_dot:0.0, pow(spec_dot,ray->obj_shine));
        tcolor[0] += spec;
        tcolor[1] += spec;
        tcolor[2] += spec;
      }
    }
  }
  tcolor[0]+= obj_color[0]*amb;
  tcolor[1]+= obj_color[1]*amb;
  tcolor[2]+= obj_color[2]*amb;
  float portion = percent*(1-ray->shade_complete);
  float * color = ray->color;
  color[0] += tcolor[0]*portion;
  color[1] += tcolor[1]*portion;
  color[2] += tcolor[2]*portion;
  ray->shade_complete+=portion;
  ray->shadow_mode = false;
}

void RayTracer::ExecuteRay (Ray * ray, int nref)
{
  if(nref>max_ref)
    return;
  ray->ref=false;
  if(Collide(ray)){
    LightShade(ray);
    if(ray->Reflect()){
      ExecuteRay(ray,nref+1);
    }
  }
}

void RayTracer::Execute (int x0, int x1, int y0, int y1, float * img,int w, int h, bool aa)
{
  printf("RayTracer::Execute x0=%d x1=%d y0=%d y1=%d img=%d w=%d h=%d\n",x0,x1,y0,y1,img,w,h);
  Ray ray; 
  ray.pposx=0;
  ray.pposy=0;
  //randomize();
  srand((unsigned)time(0));
  Ray * pray = &ray;
  int limit = aa?caster->GetRayForLimit():1;
  printf("limit=%d\n",limit);
  for(int x=x0; x<=x1; x++){
    //printf("x=%d\n",x);
    for(int y=y0; y<=y1; y++){
      for(int i=0;i<limit;i++){
        caster->Cast(pray, x, y,aa);
        ExecuteRay(pray,0);
        float * c = &img[(w*y+x)*3];
        c[0] += ray.color[0]/limit;
        c[1] += ray.color[1]/limit;
        c[2] += ray.color[2]/limit;
      }
    }
  }
}

